home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / linuxcon.000 / linuxcon / linuxconf-1.6 / translate / translat.sgml < prev    next >
SGML Document  |  1996-04-14  |  18KB

  1. <!doctype linuxdoc system>
  2. <article>
  3. <title>Translation system for Linuxconf
  4. <author>Introduction
  5.  
  6. <abstract>
  7.  
  8.     Linuxconf is a large software component, full of menus, and dialogs.
  9.     To be easily translatable, all messages must be extracted from
  10.     the C++ source code and place into dictionaries which can be
  11.     translated efficiently.
  12.  
  13.     A special set of tools has been designed to achieve this. They
  14.     are described here.
  15.  
  16. </abstract>
  17.  
  18. <sect>Introduction
  19. <p>
  20.  
  21.     This document describes both how the system works and how translators
  22.     can use it. It starts by explaining how programmers can use it
  23.     to produce translatable programs. The section "how to translate"
  24.     explains how translators can use this system to translate
  25.     Linuxconf or any programs written using this system.
  26.  
  27. <sect>Principles
  28. <p>            
  29.     To make programs easily translatable, all messages should be placed in
  30.     dictionaries. A dictionary is made of message entries. Each message
  31.     has a unique ID and a value. In the C++ source, programmers are
  32.     referring to those messages using the ID whenever they want to print
  33.     or say something.
  34.  
  35.     Each time a programmer need a new message, he has to add it in the
  36.     message dictionary and reference it from the C++ source code. This is
  37.     how most system works (There are other translation system out there).
  38.  
  39.     The system used by <em/Linuxconf/ is basically different. Messages
  40.     are defined in the <tt/C++/ source code and the dictionaries are
  41.     built by
  42.     scanning all <tt/C++/ source files. Messages are defined in the
  43.     <tt/C++/ code.
  44.     Programmers must provide and ID and a value for each message right
  45.     in the source code. This is much easier (or nicer) to do this
  46.     right in the source code than to go back and forth in the dictionary.
  47.     Furthermore, the programmer directly see the message definition
  48.     in the source. With other system, only the message ID is visible
  49.     in the source.
  50.  
  51.     Using the magic of the <tt/C/ preprocessor, the message value is
  52.     not compiled in the object code at all. Seen this way, the translation
  53.     system used by <em/Linuxconf/ yield the same result as other system.
  54.     It is just nicer to use for programmers.
  55.  
  56.     Lets describe how a programmer use the system.
  57.  
  58. <sect1>One dictionary per source directory
  59. <p>
  60.     It is best to define one message dictionary per sub-project or
  61.     sub-directory. This is easier to manage and avoid ID name space
  62.     congestion. For each directory source of <em/Linuxconf/ you have
  63.     one "<tt/dic/" file and one "<tt/m/" file. Both file are produced
  64.     simply by doing
  65.  
  66.     <tscreen><verb>
  67.     make msg
  68.     </verb></tscreen>
  69.  
  70.     This command scans all <tt/C++/ source file of the current directory
  71.     and update the file <tt>../messages/sources/DIRECTORY.dic</tt> and
  72.     the file <tt/DIRECTORY.m/, where <tt/DIRECTORY/ is the name of the
  73.     current directory.
  74.  
  75.     <tt/make msg/ use the <tt>../translate/msgscan</tt> utility to
  76.     scan the source. This utility looks for specific constructs in the
  77.     <tt/C++/ source file. Here they are.
  78.  
  79. <sect1>The <tt/MSG_U/ macro
  80. <p>
  81.  
  82.     The <tt/MSG_U/ macro defines a new message. It defines both its ID
  83.     and its value. This macro is usable anywhere a <tt/C++/ string would
  84.     be.
  85.  
  86.     <tscreen><verb>
  87.     #include "prjfoo.m"
  88.  
  89.     int foo()
  90.     {
  91.         printf (MSG_U(M_MSG1,"Entering function foo"));
  92.     }
  93.     </verb></tscreen>
  94.  
  95.     MSG_U defines a single value. <tt/U/ stands for unilingual. It
  96.     only defines one value.
  97.     
  98. <sect1>The <tt/MSG_B/ macro
  99. <p>
  100.  
  101.     The <tt/MSG_B/ macro is like the <tt/MSG_U/ macro, except it
  102.     defines two values, allowing a programmer to code immediately
  103.     two languages at once. The <tt/B/ stands for bilingual. This
  104.     has not been used in the <em/Linuxconf/ project but has proven
  105.     effective for other projects.
  106.  
  107.     <tscreen><verb>
  108.     #include "prjfoo.m"
  109.  
  110.     int foo()
  111.     {
  112.         printf (MSG_U(M_MSG1
  113.             ,"Entering function foo\n"));
  114.             ,"DΘmarrage de la fonction foo\n"));
  115.     }
  116.     </verb></tscreen>
  117.  
  118. <sect1>The <tt/MSG_R/ macro
  119. <p>
  120.  
  121.     The <tt/MSG_R/ macro simply references an already defined
  122.     message. This message may have been defined in another source
  123.     file (of the same project). Like the other macros, <tt/MSG_R/ may
  124.     be used anywhere a <tt/C++/ string is.
  125.  
  126. <sect1>The <tt/MSG_VERSION/ macro
  127. <p>
  128.     This macro has not been used so far. It would allow one programmer
  129.     to raise the version number of a dictionary, preventing older
  130.     application to use the newer potentially incompatible dictionary.
  131.  
  132.     The msgclean utility also plays with the version number of the
  133.     dictionary. The <tt/MSG_VERSION/ macro is still a concept rather
  134.     than a useful addition. Stay tune...
  135.  
  136. <sect1>The magic of the <tt/MSG_/ macros
  137. <p>
  138.     The <tt/MSG_/ macros perform two tasks. First, they are easily
  139.     spotted by the <tt/msgscan/ utility. The parsing is simple and reliable
  140.     even if the <tt/C++/ source code is not functional. Second, they
  141.     hide the retrieval mechanism (How the message value is retrieved from
  142.     the binary dictionary at runtime).
  143.  
  144.     The msgscan utility produce the <tt/.m/ file which looks like this
  145.     for the simple example above.
  146.  
  147.     <tscreen><verb>
  148.     FILE prjfoo.m:
  149.  
  150.     extern const char **_dictionary_prjfoo;
  151.     #ifndef DICTIONNARY_REQUEST
  152.         #define DICTIONNARY_REQUEST \
  153.         const char **_dictionary_prjfoo;\
  154.         TRANSLATE_SYSTEM_REQ _dictionary_req_prjfoo\
  155.             ("prjfoo",_dictionary_prjfoo,55,1);\
  156.         void dummy_dict_prjfoo(){}
  157.     #endif
  158.     #ifndef MSG_U
  159.         #define MSG_U(id,m)    id
  160.         #define MSG_B(id,m,n)    id
  161.         #define MSG_R(id)    id
  162.     #endif
  163.     #define M_MSG1    _dictionary_prjfoo[0]
  164.     </verb></tscreen>
  165.  
  166.  
  167.     As you see, one global variable is created: <tt/_dictionary_prjfoo/.
  168.     A special macro <tt/DICTIONARY_REQUEST/ is defined. This macro
  169.     should be placed in one source of the project. It is generally
  170.     place in the file <tt/_dict.c/ presented later.
  171.  
  172. <sect>How to use it
  173. <p>
  174.     To produce a translatable program, do the following
  175.  
  176.     <itemize>
  177.     <item>Replace all string message with <tt/MSG_U/ or <tt/MSG_B/
  178.         macros, giving each message a unique <tt/ID/.
  179.     <item>include (#include) the <tt/.m/ file in each source file
  180.         using the <tt/MSG_x/ macros. This file is generally named
  181.         <tt/directory.m/ where directory is the name of the current
  182.         directory.
  183.     <item>Create a file <tt/_dict.c/. The content of this file
  184.         is shown below.
  185.     <item>Use "<tt/make msg/" to extract the messages. This produces/updates
  186.         the dictionary file <tt/directory.dic/ and produces the include file
  187.         <tt/directory.m/.
  188.     <item>Compile and link your program.
  189.     <item>Use "<tt/make msg.eng/" to produce the English binary dictionary.
  190.         The file produced should be placed where your program expects it.
  191.     </itemize>
  192.  
  193.     We will now describe further the different steps involved.
  194.  
  195. <sect1>The <tt/make msg/ command and <tt/msgscan/ utility
  196. <p>
  197.     The <tt/make msg/ command invokes the <tt/msgscan/ utility. This utility
  198.     scan a set of <tt/C/ or <tt/C++/ source file, updates a dictionary
  199.     file and produces one include file.
  200.  
  201.     Here is the command use to update the dictionary of the sub-project
  202.     <tt/uucp/ of the <em/Linuxconf/ project.
  203.  
  204.     <tscreen><verb>
  205.         ../translate/msgscan uucp \
  206.             ../messages/sources/uucp.dic uucp.m EF *.c
  207.     </verb></tscreen>
  208.  
  209.     The first argument is the name of the dictionary. The second argument
  210.     is the path of the dictionary file. As you see, dictionary file are
  211.     kept in a single directory for all projects. They are seldom. This
  212.     eases the works of translators. The third argument is the path of
  213.     the include file, which is produced in the current directory.
  214.  
  215.     The fourth argument is the letter tags used to identify messages
  216.     defined with the macro <tt/MSG_U/ and <tt/MSG_B/. Messages
  217.     defined with <tt/MSG_U/ will be tagged with the letter E (English)
  218.     and messages defined with <tt/MSG_B/ will be tagged with
  219.     <tt/E/ for the first value and <tt/F/ (French) for the second.
  220.  
  221. <sect1>The <tt/_dict.c/ file
  222. <p>
  223.  
  224.     It is good practice to place the DICTIONARY_REQUEST macro
  225.     in a file _dict.c. There is generally one such a file per
  226.     directory. Its contents is generally:
  227.  
  228.     <tscreen><verb>
  229.     #include "this_directory.m"
  230.     #include <translat.h>
  231.     DICTIONARY_REQUEST
  232.     </verb></tscreen>
  233.  
  234.     At least this dependency should be placed in your <tt/makefile/
  235.  
  236.     <tscreen><verb>
  237.     _dict.o: _dict.c this_directory.m
  238.     </verb></tscreen>
  239.  
  240.     This will ensure that each time you update your dictionary (and
  241.     the <tt/m/ header file), <tt/_dict.c/ will be recompile, ensuring
  242.     proper recording of the dictionary revision and number of message.
  243.     This will avoid executing a program with an obsolete
  244.     or incompatible binary dictionary.
  245.  
  246.     Given that <tt/_dict.c/ is small, the compilation is pretty
  247.     short.
  248.  
  249. <sect1>The <tt/msgcomp/ utility
  250. <p>
  251.  
  252.     Once you have compiled and linked your program, you must "compiled"
  253.     all the dictionaries used in your program into one binary dictionary.
  254.     This is done by the <tt/msgcomp/ utility. Here is the command
  255.     used when doing "<tt/make msg.eng/" for the <em/Linuxconf/ project.
  256.     This produces the English binary dictionary.
  257.  
  258.     <tscreen><verb>
  259.     ../translate/msgcomp -p../messages/sources/ \
  260.         /tmp/linuxconf-msg-1.3.eng eE \
  261.         askrunlevel dialog dnsconf fstab \
  262.         misc main netconf mailconf uucp userconf
  263.     </verb></tscreen>
  264.  
  265.     This commands take all dictionaries for sub-projects 
  266.         <tt/askrunlevel dialog dnsconf fstab misc main netconf mailconf
  267.         uucp/ and <tt/userconf/ and produce a single binary dictionary.
  268.  
  269.     The <tt/-p/ option tells msgcomp to look for those dic files (
  270.         askrunlevel.dic dialog.dic ...)
  271.         in the directory <tt>../messages/sources/</tt>.
  272.  
  273.     The argument <tt>/tmp/linuxconf-msg-1.3.eng</tt> is the file to produce.
  274.     The argument <tt/eE/ instructs <tt/msgcomp/ to extract message's values with
  275.     the '<tt/e/' tag. If there is no such value for a given message, the
  276.     value with the '<tt/E/' tag will be used.
  277.  
  278. <sect2>Convention used for tags
  279. <p>
  280.     Dictionary file contain the definition for all messages. Each messages
  281.     may have different values, identified by a tag letter. When messages
  282.     are extracted by msgscan, it is instructed to associate values with
  283.     given tags. By convention, we use upper case letter to identify
  284.     message's value extracted from the source code. Lower case value
  285.     are used by translators.
  286.  
  287.     We assume here that programmers are bad writers. We let them give
  288.     their best shots for messages and we are allowed to override their
  289.     work without overwriting it. By giving precedence to '<tt/e/' tags
  290.     over '<tt/E/' we are saying that translators work override the
  291.     work of programmers, but we are not forcing the translators to
  292.     rewrite everything.
  293.         
  294. <sect1>The <tt/msgclean/ utility
  295. <p>
  296.     The <tt/msgscan/ utility maintains dictionary. At some point
  297.     some messages may become obsolete (Unused in any source files). The
  298.     <tt/msgclean/ is used to clean messages without values in the
  299.     <tt/dic/ file.
  300.  
  301.     For the <em/Linuxconf/ project, the <tt/make/ target <tt/msg.clean/
  302.     is defined for that purpose.
  303.  
  304.     Be aware that applying msgclean on a dictionary file with obsolete
  305.     message has an important side effect. Some message being deleted, the
  306.     numbering of all following message will be changed. All source using
  307.     the <tt/m/ include file should be recompiled.
  308.  
  309.     To avoid problems, the <tt/msgclean/ utility automaticly increases
  310.     the revision number of the dictionary. This prevents using a
  311.     dictionary with an incompatible program.
  312.  
  313. <sect>Usage restriction
  314. <p>
  315.     The strategy used is mainly targeted at <tt/C++/ code. With some
  316.     restriction, it may be used for <tt/C/ code. Here are the main
  317.     feature that probably don't work with <tt/C/.
  318.  
  319.     <descrip>
  320.     <tag/static initialization/
  321.  
  322.         In <tt/C++/ one can write the following code.
  323.  
  324.         <tscreen><verb>
  325.         static char *tb[]={
  326.             foo(1),foo(22)
  327.         };
  328.         </verb></tscreen>
  329.  
  330.         where foo is a function. The <tt/C++/ compiler will generate
  331.         the proper code which will be probably called once. The <tt/MSG_U/
  332.         macro (and others) are not hiding function call, but are indeed
  333.         dynamic in some sens. <tt/C/ does not support this. Other
  334.         translation strategy based on dictionary do have the same
  335.         limitation though.
  336.  
  337.     </descrip>
  338.  
  339.     The example using the <tt/static char *tb[]/ is also causing a problem
  340.     in <tt/C++/ if the variable is declared outside of a function. The
  341.     problem appear because the "hidden" initialization code generated
  342.     by the compiler is called very early, often before <tt/main()/ is called.
  343.     Normally, the function <tt/translat_load()/ which bring the
  344.     dictionary in memory is called by <tt/main()/.
  345.  
  346.     Fortunately, the current implementation, where <tt/_dictionary_system/
  347.     is a pointer will trigger a <tt/seg fault/ whenever this condition
  348.     is met. This fault will be trigger all the time, because all
  349.     initialization are called before main. The strategy is <em/safe/.
  350.  
  351. <sect>Recommend usage and convention
  352. <p>
  353.  
  354. <sect1>Naming convention for message's ID
  355. <p>
  356.     To help peoples who will translate your <em/Linuxconf/, I have used
  357.     a convention for the ID's name.
  358.  
  359.     <descrip>
  360.     <tag/B_/
  361.  
  362.         Buttons.
  363.  
  364.     <tag/E_/
  365.  
  366.         Error message start with this.
  367.  
  368.     <tag/F_/
  369.  
  370.         Field labels start with this.
  371.  
  372.     <tag/I_/
  373.  
  374.         Dialog introduction start with this.
  375.  
  376.     <tag/M_/
  377.  
  378.         All menu entries start with this prefix.
  379.  
  380.     <tag/N_/
  381.  
  382.         Notices and warning start with this.
  383.  
  384.     <tag/P_/
  385.  
  386.         When the user is prompted for a password, the message's ID
  387.         start with this.
  388.  
  389.     <tag/Q_/
  390.  
  391.         Identify a question (Generally a Yes/No prompt).
  392.  
  393.     <tag/T_/
  394.  
  395.         Dialog's title start with this.
  396.  
  397.     <tag/X_/
  398.  
  399.         All other messages which fit in no category.
  400.  
  401.     </descrip>
  402.  
  403. <sect>How to translate
  404. <p>
  405. <sect1>Go simple
  406. <p>
  407.     One way to translate is to go right in the <tt/.dic/ files and add
  408.     translations for each message using a different tag. Then use
  409.     the <tt/msgcomp/ utility to extract the proper definition.
  410.  
  411.     At first, there is little problem doing this. The <tt/msgscan/ utility
  412.     read,update and save the <tt/.dic/ file, so your changes won't be
  413.     lost.
  414.  
  415.     The problem come from the way software is developed. First we develop
  416.     and then, when it is stable, we translate. Doing so mean that we have
  417.     to walk all the <tt/.dic/ files to make sure our translation still
  418.     fit with the original messages (English version for example). Those
  419.     original messages may have changed.
  420.  
  421.     A different scheme was chosen for <em/Linuxconf/.
  422.  
  423. <sect1>Organization of the <tt/messages/ directory
  424. <p>
  425.     The <tt/messages/ directory contain one subdirectory per language
  426.     plus one <tt/sources/ directory. This directory contains all the
  427.     <tt/.dic/ files produced by scanning the <tt/C++/ source files.
  428.     These file are never hand edited.
  429.  
  430.     Each other directory has a copy of those <tt/.dic/ files with
  431.     the proper translation. A special utility <tt/msgupd/ has been
  432.     created: it basicly compared all messages in the <tt/sources/
  433.     directory with messages in the translated directory. It compare
  434.     only one language (say the English version).
  435.  
  436.     Mostly, <tt/msgupd/ will tell you
  437.  
  438.     <itemize>
  439.     <item>Which messages are new.
  440.     <item>Which messages have changed (The English wording).
  441.     </itemize>
  442.  
  443.     Using that information, you know exactly what you have to do to
  444.     keep your work in sync with the current release of <em/Linuxconf/.
  445.     <tt/msgupd/ will reorder the translated <tt/.dic/ file (Not the
  446.     one in the <tt/sources/ directory) so all messages which needed
  447.     work are at the beginning of the file. It also add a comment (<tt/.dic/
  448.     files may have comments like most normal <em/Unix/ configuration file)
  449.     explaining what have to be done.
  450.  
  451.     If the English version of the message was changed, it will re tag the
  452.     version in the translated file and add the new version, plus a comment.
  453.     The old English message will have the tag "<tt/Z/". You can see
  454.     easily what is the change.
  455.  
  456. <sect1>The <tt/msgupd/ utility
  457. <p>
  458.     The file <tt/rules.mak/ shows the rules for one translation (which
  459.     is not done yet). Look for the target <tt/msg.cfr/ and <tt/upd.cfr/.
  460.     To add a new language, do this
  461.  
  462.     <itemize>
  463.     <item>Create a new directory empty in the <tt/messages/ directory, for
  464.         example, <tt/mar/ for <em/Alien language/.
  465.     <item>Customize <tt/rules.mak/ and add the target <tt/msg.mar/
  466.         and <tt/upd.mar/.
  467.     <item>Run the following command. This will fill the
  468.         <tt>messages/mar</tt> directory with all the necessary <tt/.dic/
  469.         files.
  470.  
  471.         <tscreen><verb>        
  472.         make upd.mar
  473.         </verb></tscreen>
  474.  
  475.     <item>Go into <tt>messages/mar</tt> and edit each <tt/.dic/ file and
  476.         add the proper translation as needed.
  477.     <item>Run the following command to produce the binary dictionary
  478.         required to run <em/Linuxconf/.
  479.  
  480.         <tscreen><verb>        
  481.         make msg.mar
  482.         </verb></tscreen>
  483.     <item>Set the following environment variable and run <em/Linuxconf/.
  484.  
  485.         <itemize>
  486.         <item>export LINUXCONF_LANG=mar
  487.         <item>export LINUXCONF_DICT=/tmp
  488.  
  489.                 This variable is optional. <em/Linuxconf/ will normally
  490.                 look for its message dictionary in
  491.                 <tt>/usr/lib/linuxconf</tt>. This variable override this.
  492.                 The <tt/msg.*/ makefile's target generally produce
  493.                 their output in /tmp. This is useful to test new messages
  494.                 without breaking the current installation of <em/Linuxconf/.
  495.  
  496.                 Be aware that this mechanism only work if you execute
  497.                 <em/Linuxconf/ as root. For security reason, a normal
  498.                 user can't override the message dictionary of
  499.                 <em/Linuxconf/ (Although he can select a different
  500.                 language from <tt>/usr/lib/linuxconf</tt> if available).
  501.         </itemize>
  502.     </itemize>
  503.  
  504. <sect1>The <tt/msgcomp/ utility
  505. <p>
  506.     The msgcomp utility has been tweaked to support the distribute
  507.     directory concept. Mainly it use the <tt/.dic/ file in the
  508.     <tt/sources/ directory as a reference. Message number ID are
  509.     defined from this file. It then used (optionally) alternative
  510.     <tt/.dic/ file to grab extra translations. The ordering of the
  511.     <tt/.dic/ file is unimportant.
  512.  
  513. <sect>Licensing
  514. <p>
  515.     The <em/translate/ directory is part of the <em/Linuxconf/ project
  516.     but carry a special license. There is no restriction on usage. Feel
  517.     free to incorporate this system to any project.
  518.  
  519.     This simple license does not apply to the rest of <em/Linuxconf/
  520.     which is covered by the standard GNU Copyleft license. See the
  521.     file <tt/LICENSE/ in the root directory.
  522.  
  523.     If you find it useful for other project, send me a note and some
  524.     comments if possible.
  525.  
  526. </article>
  527.  
  528.